package com.itheima.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.URLEncoder;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.common.DefaultException;
import com.itheima.convert.OrganizationConvert;
import com.itheima.convert.UserConvert;
import com.itheima.dto.MidDto;
import com.itheima.dto.OrganizationDto;
import com.itheima.dto.RoleDto;
import com.itheima.entity.Organization;
import com.itheima.entity.User;
import com.itheima.enums.OrganizationEnum;
import com.itheima.enums.RedisEnum;
import com.itheima.mapper.*;
import com.itheima.service.OrganizationService;
import com.itheima.service.UserService;
import com.itheima.utils.CheckUtil;
import com.itheima.utils.OrganizationTreeUtil;
import com.itheima.utils.RedisUtil;
import com.itheima.vo.organization.OrganizationBriefVo;
import com.itheima.vo.organization.OrganizationDetailVo;
import com.itheima.vo.organization.OrganizationExcelVo;
import com.itheima.vo.organization.OrganizationVo;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author 51585
 * @description 针对表【organization】的数据库操作Service实现
 * @createDate 2022-10-05 17:11:24
 */
@Service
public class OrganizationServiceImpl extends ServiceImpl<OrganizationMapper, Organization>
        implements OrganizationService {
    @Resource
    private OrganizationTreeUtil organizationTreeUtil;
    @Resource
    private UserService userService;
    @Resource
    private MessageMapper messageMapper;
    @Resource
    private UserLikeMessageMapper userLikeMessageMapper;
    @Resource
    private NoticeMapper noticeMapper;
    @Resource
    private UserLinkNoticeMapper userLinkNoticeMapper;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private UserConvert userConvert;
    @Resource
    private OrganizationConvert organizationConvert;

    private final boolean FALSE = false;

    /**
     * 查找当前用户能向下管理的权限
     *
     * @return
     */
    @Override
    public OrganizationVo findRole() throws Exception {
        String userId = CheckUtil.checkLogin();
        User user = userService.getById(userId);
        Object o = redisTemplate.opsForValue().get(RedisEnum.ORGANIZATION_STRUCTURE.getRedis() + user.getOrganizationId().toString());
        OrganizationVo vo;
        if (StringUtils.isEmpty(o)) {
            vo = organizationTreeUtil.createByOrganization(user.getOrganizationId());
            redisTemplate.opsForValue().set(RedisEnum.ORGANIZATION_STRUCTURE.getRedis() + user.getOrganizationId().toString(), JSONUtil.toJsonStr(vo));
        } else {
            vo = JSONObject.parseObject(o.toString(), OrganizationVo.class);
        }
        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateRole(RoleDto roleDto) throws Exception {
        String userId = CheckUtil.checkLogin();
        Integer userOrganizationId = userService.getById(userId).getOrganizationId();
//        Integer userOrganizationId = 2;
        if (StringUtils.isEmpty(userOrganizationId)) {
            throw new DefaultException("没有该组织");
        }
        List<String> userIds = roleDto.getUserIds();
        Integer organizationId = roleDto.getOrganizationId();
        Organization organization = this.lambdaQuery()
                .select(Organization::getId)
                .eq(Organization::getId, organizationId)
                .one();
        Optional.ofNullable(organization).orElseThrow(()->new DefaultException("没有该组织，请检查"));
        List<User> users = userService.lambdaQuery()
                .in(User::getId, userIds)
                .isNull(User::getDeletedAt)
                .list();
        if (!userOrganizationId.equals(OrganizationEnum.administrators.getOrganizationId())
                && !userOrganizationId.equals(OrganizationEnum.school.getOrganizationId())) {
                throw new DefaultException("权限不足，修改失败");
        }
        LocalDateTime now = LocalDateTime.now();
        List<User> collect = users.stream().map(user -> {
            user.setOrganizationId(organizationId);
            user.setUpdatedAt(now);
            return user;
        }).collect(Collectors.toList());
        userService.updateBatchById(collect);
        RedisUtil.deleteOrganizationStructure();
    }

    @Override
    public void insertOrganization(OrganizationDto organizationDto) throws Exception {
        String userId = CheckUtil.checkLogin();
//        String userId = "19251104253";
        List<Organization> list = this.lambdaQuery()
                .select(Organization::getId)
                .eq(Organization::getName, organizationDto.getName())
                .isNull(Organization::getDeletedAt)
                .list();
        if (!CollectionUtils.isEmpty(list)) {
            throw new DefaultException("组织名称重复，请更改");
        }
        checkPower(userId, organizationDto.getOrganizationId());
        Organization organization = convertToOrganization(organizationDto);
        doSave(organization);
        RedisUtil.deleteOrganizationStructure();
    }

    @Override
    public void updateOrganization(OrganizationDto organizationDto) throws Exception {
        String userId = CheckUtil.checkLogin();
//        String userId = "19251104253";
        checkPower(userId, organizationDto.getOrganizationId());
        boolean update = this.lambdaUpdate()
                .set(Organization::getName, organizationDto.getName())
                .set(Organization::getUpdatedAt, LocalDateTime.now())
                .eq(Organization::getId, organizationDto.getOrganizationId())
                .update();
        if (!update) {
            throw new DefaultException("更新失败");
        }
        RedisUtil.deleteOrganizationStructure();
    }


    @Override
    public void deleteOrganization(Integer organizationId) throws Exception {
        String userId = CheckUtil.checkLogin();
//        String userId = "19251104253";
        Organization organization = this.getById(organizationId);
        Optional.ofNullable(organization).orElseThrow(() -> new DefaultException("组织不存在"));
        checkUserBelong(organizationId);
        checkLeafNode(organizationId);
        checkPower(userId, organizationId);
        doDelete(organizationId);
        RedisUtil.deleteOrganizationStructure();
    }

    @Override
    public OrganizationDetailVo showDetail(Integer organizationId) throws DefaultException {
        Organization organization = this.getById(organizationId);
        if (Objects.isNull(organization)) {
            throw new DefaultException("没有当前组织");
        }
        List<User> userList = userService.lambdaQuery()
                .select(User::getId, User::getName, User::getImageUrl)
                .eq(User::getOrganizationId, organizationId)
                .isNull(User::getDeletedAt)
                .list();
        List<OrganizationDetailVo.OrganizationUser> organizationUsers = userConvert.toOrganizationUser(userList);
        OrganizationDetailVo organizationDetailVo = organizationConvert.toOrganizationDetailVo(organization);
        organizationDetailVo.setUserList(organizationUsers);
        return organizationDetailVo;
    }

    @Override
    public List<OrganizationBriefVo> listOrganization(String keyword) {
        List<Organization> list = this.lambdaQuery()
                .select(Organization::getId, Organization::getName)
                .like(StringUtils.isEmpty(keyword), Organization::getName, keyword)
                .isNull(Organization::getDeletedAt)
                .list();
        return organizationConvert.toOrganizationBriefVo(list);
    }

    @Override
    public void excelExport(Integer organizationId, HttpServletResponse response) throws IOException {
        List<User> userList = userService.lambdaQuery()
                .select(User::getId, User::getName, User::getPhone, User::getCreatedAt)
                .eq(User::getOrganizationId, organizationId)
                .list();
        List<String> userIdList = userList.stream().map(User::getId).collect(Collectors.toList());
        List<OrganizationExcelVo> voList = organizationConvert.toOrganizationExcelVo(userList);
        Organization organization = this.getById(organizationId);
        Map<String, Integer> messageMap = messageMapper.totalNum(userIdList).stream().collect(Collectors.toMap(MidDto::getUserId, MidDto::getNum));
        Map<String, Integer> sendLikeCountMap = userLikeMessageMapper.totalSendLikeCount(userIdList).stream().collect(Collectors.toMap(MidDto::getUserId, MidDto::getNum));
        Map<String, Integer> acceptLikeCountMap = messageMapper.totalAcceptLikeCount(userIdList).stream().collect(Collectors.toMap(MidDto::getUserId, MidDto::getNum));
        Map<String, Integer> sendNoticeNumMap = noticeMapper.totalSendNoticeNum(userIdList).stream().collect(Collectors.toMap(MidDto::getUserId, MidDto::getNum));
        Map<String, Integer> acceptNoticeNumMap = userLinkNoticeMapper.totalAcceptNoticeNum(userIdList).stream().collect(Collectors.toMap(MidDto::getUserId, MidDto::getNum));
        voList.forEach(vo -> {
            vo.setOrganizationName(organization.getName());
            vo.setMessageNum(Optional.ofNullable((messageMap.get(vo.getUserId()))).orElse(0));
            vo.setSendLikeCount(Optional.ofNullable(sendLikeCountMap.get(vo.getUserId())).orElse(0));
            vo.setAcceptLikeCount(Optional.ofNullable(acceptLikeCountMap.get(vo.getUserId())).orElse(0));
            vo.setSendNoticeNum(Optional.ofNullable(sendNoticeNumMap.get(vo.getUserId())).orElse(0));
            vo.setAcceptNoticeNum(Optional.ofNullable(acceptNoticeNumMap.get(vo.getUserId())).orElse(0));
        });
        String fileName = organization.getName() + "-名单-" + LocalDate.now();
        String fileNameUrl = URLEncoder.ALL.encode(fileName, StandardCharsets.UTF_8);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileNameUrl + ".xls");
        response.setHeader("Access-Control-Expose-Headers", "Content-disposition");
        ExcelWriter excelWriter = null;
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            excelWriter = EasyExcel.write(outputStream).build();

            // 表头居中
            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
            // 内容居中
            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

            HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                    new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

            WriteSheet sheet = EasyExcel.writerSheet(1, "用户明细")
                    .head(OrganizationExcelVo.class)
                    .registerWriteHandler(horizontalCellStyleStrategy)
                    .build();
            excelWriter.write(voList, sheet);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            excelWriter.finish();
            IoUtil.close(outputStream);
        }
    }

    public void checkUserBelong(Integer organizationId) throws DefaultException {
        Integer count = userService.lambdaQuery()
                .eq(User::getOrganizationId, organizationId)
                .count();
        if (count > 1) {
            throw new DefaultException("当前组织下有用户，无法删除");
        }
        RedisUtil.deleteOrganizationStructure();
    }

    @Transactional(rollbackFor = Exception.class)
    public void doDelete(Integer organizationId) throws DefaultException {
        Organization organization = this.lambdaQuery()
                .select(Organization::getParentId)
                .eq(Organization::getId, organizationId)
                .one();
        boolean update = this.lambdaUpdate()
                .set(Organization::getDeletedAt, LocalDateTime.now())
                .eq(Organization::getId, organizationId)
                .isNull(Organization::getDeletedAt)
                .update();
        if (!update) {
            throw new DefaultException("删除失败");
        }
        // 判断删除后，当前组织的父组织是否还有叶子组织
        Integer count = this.lambdaQuery()
                .eq(Organization::getParentId, organization.getParentId())
                .isNull(Organization::getDeletedAt)
                .count();
        if (count < 1) {
            boolean update1 = this.lambdaUpdate()
                    .set(Organization::getIsChild, 1)
                    .eq(Organization::getId, organization.getParentId())
                    .isNull(Organization::getDeletedAt)
                    .update();
            if (!update1) {
                throw new DefaultException("删除失败");
            }
        }
        RedisUtil.deleteOrganizationStructure();
    }

    /**
     * 判断当前组织是否有叶子组织
     *
     * @param organizationId
     */
    public void checkLeafNode(Integer organizationId) throws DefaultException {
        Organization organization = this.lambdaQuery()
                .select(Organization::getIsChild)
                .eq(Organization::getId, organizationId)
                .isNull(Organization::getDeletedAt)
                .one();
        if (StringUtils.isEmpty(organization)) {
            throw new DefaultException("没有该组织");
        }
        if (organization.getIsChild() == 0) {
            throw new DefaultException("当前组织存在子组织，无法删除");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void doSave(Organization organization) {
        //更新父组织的组织关系
        this.lambdaUpdate()
                .set(Organization::getIsChild, 0)
                .set(Organization::getUpdatedAt, LocalDateTime.now())
                .eq(Organization::getId, organization.getParentId())
                .update();
        this.save(organization);
    }

    public Organization convertToOrganization(OrganizationDto organizationDto) {
        Organization parentOrganization = this.lambdaQuery()
                .select(Organization::getLevel)
                .eq(Organization::getId, organizationDto.getOrganizationId())
                .one();
        Organization organization = new Organization();
        organization.setName(organizationDto.getName());
        organization.setParentId(organizationDto.getOrganizationId());
        organization.setIsChild(1);
        organization.setLevel(parentOrganization.getLevel() + 1);
        organization.setCreatedAt(LocalDateTime.now());
        return organization;
    }

    /**
     * 判断是否有权限
     *
     * @param userId
     * @param organizationId
     * @throws DefaultException
     */
    private void checkPower(String userId, Integer organizationId) throws DefaultException {
        User user = userService.getById(userId);
        Organization organization = this.getById(user.getOrganizationId());
        Organization organization1 = this.getById(organizationId);
        if (Objects.isNull(organization) || Objects.isNull(organization1)) {
            throw new DefaultException("没有该组织，请检查");
        }
        if (!user.getOrganizationId().equals(OrganizationEnum.administrators.getOrganizationId())
                && !user.getOrganizationId().equals(OrganizationEnum.school.getOrganizationId())) {
            throw new DefaultException("权限不足");
        }
    }

}




